#include "tdglwidget.h"
#include "stb_image.h"
#include <iostream>
#include <QTime>
#include <QMouseEvent>

extern const char* vertexShaderCode;
extern const char* fragmentShaderCode;
extern const char* lampVertexShaderCode;
extern const char* lampFragmentShaderCode;

unsigned int SCR_WIDTH = 800;
unsigned int SCR_HEIGHT = 600;

struct draw_type DT;

struct animation_state AS;

struct texture_state TS;

struct move_type MT;

struct light_type LT;

// for cover mode
float vertices_tex[] = {
    // positions          // normals           // texture coords
    -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,
    0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  0.0f,
    0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
    0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
    -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,

    -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,
    0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  0.0f,
    0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
    0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
    -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,

    -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
    -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
    -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
    -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
    -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
    -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,

    0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
    0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
    0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
    0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
    0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
    0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,

    -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,
    0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  1.0f,
    0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
    0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,

    -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,
    0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  1.0f,
    0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
    0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f
};
// for line mode: don't need normals and texture coords
float vertices[] = {
    -0.5f,  0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f, 0.0f,
    0.5f,  0.5f,  0.5f,   0.0f, 0.0f, 0.0f,
    0.5f,  0.5f, -0.5f,   0.0f, 0.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 0.0f, 0.0f,

    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
    0.5f, -0.5f, -0.5f,   0.0f, 0.0f, 0.0f,
    0.5f, -0.5f,  0.5f,   0.0f, 0.0f, 0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,

    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
    0.5f,  0.5f, -0.5f,   0.0f, 0.0f, 0.0f,
    0.5f, -0.5f, -0.5f,   0.0f, 0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,

    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 0.0f,
    0.5f, -0.5f,  0.5f,   0.0f, 0.0f, 0.0f,
    0.5f,  0.5f,  0.5f,   0.0f, 0.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f, 0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 0.0f,

    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f, 0.0f,

    0.5f,  0.5f,  0.5f,   0.0f, 0.0f, 0.0f,
    0.5f, -0.5f,  0.5f,   0.0f, 0.0f, 0.0f,
    0.5f, -0.5f, -0.5f,   0.0f, 0.0f, 0.0f,
    0.5f,  0.5f, -0.5f,   0.0f, 0.0f, 0.0f,
    0.5f,  0.5f,  0.5f,   0.0f, 0.0f, 0.0f
};

tdglWidget::tdglWidget(QWidget *parent): camera(QVector3D(0.0f, 0.0f, 3.0f), QVector3D(0.0f, 1.0f, 0.0f))
{
    Q_UNUSED(parent);

    angle = 0.0;//40.0f;
    model.setToIdentity();
    model.rotate(angle, QVector3D(1.0f, 1.0f, 0.0f));
    projection.setToIdentity();
    view.setToIdentity();
    projection.perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);

    // fixed light position
    lightPos.setX(1.5f);
    lightPos.setY(1.3f);
    lightPos.setZ(2.5f);
}

tdglWidget::~tdglWidget() {
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
}

void tdglWidget::initializeGL()
{
    initializeOpenGLFunctions();

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glGenVertexArrays(1, &lightVAO);
    glGenBuffers(1, &lightVBO);

    installShader();
    initTexture();
}

void tdglWidget::installShader()
{
    initializeOpenGLFunctions();

    vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
    fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

    const char* adapter[1];
    adapter[0] = vertexShaderCode;
    glShaderSource(vertexShaderID, 1, adapter, 0);
    adapter[0] = fragmentShaderCode;
    glShaderSource(fragmentShaderID, 1, adapter, 0);

    glCompileShader(vertexShaderID);
    // check
    GLint success;
    GLchar infoLog[1024];
    glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShaderID, 1024, NULL, infoLog);
        std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: vertexShader\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
    }
    glCompileShader(fragmentShaderID);
    // check
    glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShaderID, 1024, NULL, infoLog);
        std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: fragmentShader\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
    }

    programID = glCreateProgram();
    glAttachShader(programID, vertexShaderID);
    glAttachShader(programID, fragmentShaderID);

    glLinkProgram(programID);
    // check
    glGetProgramiv(programID, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(programID, 1024, NULL, infoLog);
        std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: program\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
    }
    glUseProgram(programID);

    glDeleteShader(vertexShaderID);
    glDeleteShader(fragmentShaderID);

    // ------------
    // lamp shader
    lampVertexShaderID = glCreateShader(GL_VERTEX_SHADER);
    lampFragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

    adapter[0] = lampVertexShaderCode;
    glShaderSource(lampVertexShaderID, 1, adapter, 0);
    adapter[0] = lampFragmentShaderCode;
    glShaderSource(lampFragmentShaderID, 1, adapter, 0);

    glCompileShader(lampVertexShaderID);
    // check
    glGetShaderiv(lampVertexShaderID, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(lampVertexShaderID, 1024, NULL, infoLog);
        std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: lamp vertexShader\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
    }
    glCompileShader(lampFragmentShaderID);
    // check
    glGetShaderiv(lampFragmentShaderID, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(lampFragmentShaderID, 1024, NULL, infoLog);
        std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: lamp fragmentShader\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
    }

    lampProgramID = glCreateProgram();
    glAttachShader(lampProgramID, lampVertexShaderID);
    glAttachShader(lampProgramID, lampFragmentShaderID);

    glLinkProgram(lampProgramID);
    // check
    glGetProgramiv(lampProgramID, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(lampProgramID, 1024, NULL, infoLog);
        std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: lamp program\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
    }
    glDeleteShader(lampVertexShaderID);
    glDeleteShader(lampFragmentShaderID);
}

void tdglWidget::initTexture() {
    initializeOpenGLFunctions();
    // texture 1
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1);
    // set the texture wrapping parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // set texture filtering parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // load image, create texture and generate mipmaps
    int width, height, nrChannels;
    // tell stb_image.h to flip loaded texture's on the y-axis.
    stbi_set_flip_vertically_on_load(true);
    unsigned char *data = stbi_load("./textures/brickwall.jpg", &width, &height, &nrChannels, 0);
    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);
    // texture 2
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // set the texture wrapping parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // set texture filtering parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // load image, create texture and generate mipmaps
    data = stbi_load("./textures/albedo.png", &width, &height, &nrChannels, 0);
    if (data)
    {
        // note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBA
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    }
    stbi_image_free(data);

    // tell opengl for each sampler to which texture unit it belongs to (only has to be done once)
    glUniform1i(glGetUniformLocation(programID, "texture1"), 0);
    glUniform1i(glGetUniformLocation(programID, "texture2"), 1);
}

void tdglWidget::paintGL()
{
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    //glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    //glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glEnable(GL_DEPTH_TEST);

    // const qreal retinaScale = devicePixelRatio();
    // glViewport(0, 0, width() * retinaScale, height() * retinaScale);
    glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);

    if (LT.l_type == 1 && DT.type == 3) {
        // render the lamp : only when cover-mode and point-light enabled
        glUseProgram(lampProgramID);
        projection.setToIdentity();
        projection.perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        glUniformMatrix4fv(glGetUniformLocation(lampProgramID, "projection"), 1, GL_FALSE, projection.data());
        view = camera.GetViewMatrix();
        glUniformMatrix4fv(glGetUniformLocation(lampProgramID, "view"), 1, GL_FALSE, view.data());
        QMatrix4x4 m;
        m.setToIdentity();
        m.translate(lightPos);
        m.scale(0.2f); // lamp : a smaller cube
        glUniformMatrix4fv(glGetUniformLocation(lampProgramID, "model"), 1, GL_FALSE, m.data());
        // bind data
        glBindVertexArray(lightVAO);
        glBindBuffer(GL_ARRAY_BUFFER, lightVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_tex), vertices_tex, GL_STATIC_DRAW);
        // position attribute
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        glBindVertexArray(lightVAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
    }

    glUseProgram(programID);
    projection.setToIdentity();
    projection.perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
    glUniformMatrix4fv(glGetUniformLocation(programID, "projection"), 1, GL_FALSE, projection.data());
    view = camera.GetViewMatrix();
    glUniformMatrix4fv(glGetUniformLocation(programID, "view"), 1, GL_FALSE, view.data());
    if (AS.isAnimating) {
        // animation
        angle = 0.5f;
        model.rotate(angle, QVector3D(1.0f, 1.0f, 0.0f));
    }
    glUniformMatrix4fv(glGetUniformLocation(programID, "model"), 1, GL_FALSE, model.data());

    // default to draw with one color
    glUniform1i(glGetUniformLocation(programID, "texType"), 0);
    float t[] = {TS.orgColor.x(), TS.orgColor.y(), TS.orgColor.z()};
    glUniform3fv(glGetUniformLocation(programID, "orgColor"), 1, t);
    if (DT.type == 3) {
        // bind textures on corresponding texture units
        if (TS.tex1 && !TS.tex2) {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, texture1);
            glUniform1i(glGetUniformLocation(programID, "texType"), 1);
            glUniform1i(glGetUniformLocation(programID, "texNum"), 1);
            glUniform1i(glGetUniformLocation(programID, "material.texture_diffuse"), 0);
        }
        if (TS.tex2 && !TS.tex1) {
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, texture2);
            glUniform1i(glGetUniformLocation(programID, "texType"), 2);
            glUniform1i(glGetUniformLocation(programID, "texNum"), 2);
            glUniform1i(glGetUniformLocation(programID, "material.texture_diffuse"), 1);
        }
        if (TS.tex1 && TS.tex2) {
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, texture1);
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, texture2);
            glUniform1i(glGetUniformLocation(programID, "texType"), 3);
            glUniform1i(glGetUniformLocation(programID, "texNum"), 3);
            // mixture : To in shader
        }
        if (!TS.tex1 && !TS.tex2) {
            // no texture
            glUniform1i(glGetUniformLocation(programID, "texType"), 0);
            glUniform1i(glGetUniformLocation(programID, "texNum"), 0);
        }

        if (LT.l_type == 1) { // point light
            glUniform1i(glGetUniformLocation(programID, "texType"), 4);
            t[0] = lightPos.x();
            t[1] = lightPos.y();
            t[2] = lightPos.z();
            glUniform3fv(glGetUniformLocation(programID, "light.position"), 1, t);
            t[0] = camera.Position.x();
            t[1] = camera.Position.y();
            t[2] = camera.Position.z();
            glUniform3fv(glGetUniformLocation(programID, "viewPos"), 1, t);
            // light properties 光线衰减使可视距离为50单位
            glUniform1f(glGetUniformLocation(programID, "light.constant"), 1.0f);
            glUniform1f(glGetUniformLocation(programID, "light.linear"), 0.09f);
            glUniform1f(glGetUniformLocation(programID, "light.quadratic"), 0.032f);
//            // 对具体的物体使用全色光源使物体质感明显
//            t[0] = 1.0f;
//            t[1] = 1.0f;
//            t[2] = 1.0f;
            t[0] = 0.5f;
            t[1] = 0.5f;
            t[2] = 0.5f;
            glUniform3fv(glGetUniformLocation(programID, "light.ambient"), 1, t);
            t[0] = 0.8f;
            t[1] = 0.8f;
            t[2] = 0.8f;
            glUniform3fv(glGetUniformLocation(programID, "light.diffuse"), 1, t);
            t[0] = 1.0f;
            t[1] = 1.0f;
            t[2] = 1.0f;
            glUniform3fv(glGetUniformLocation(programID, "light.specular"), 1, t);
            t[0] = TS.orgColor.x();
            t[1] = TS.orgColor.y();
            t[2] = TS.orgColor.z();
//            // material properties : Cyan Plastic(青色塑料)
//            t[0] = 0.0f;
//            t[1] = 0.1f;
//            t[2] = 0.06f;
            glUniform3fv(glGetUniformLocation(programID, "material.ambient"), 1, t);
//            t[0] = 0.0f;
//            t[1] = 0.50980392f;
//            t[2] = 0.50980392f;
            glUniform3fv(glGetUniformLocation(programID, "material.diffuse"), 1, t);
//            t[0] = 0.50196078f;
//            t[1] = 0.50196078f;
//            t[2] = 0.50196078;
            // 适当弱化镜面光
            t[0] = 0.5f;
            t[1] = 0.5f;
            t[2] = 0.5f;
            glUniform3fv(glGetUniformLocation(programID, "material.specular"), 1, t);
            glUniform1f(glGetUniformLocation(programID, "material.shininess"), 32.0f);
        } else if (LT.l_type == 2) { // directional light
            glUniform1i(glGetUniformLocation(programID, "texType"), 5);
            t[0] = -0.2f;
            t[1] = -1.0f;
            t[2] = -0.3f;
            glUniform3fv(glGetUniformLocation(programID, "light.direction"), 1, t);
            t[0] = camera.Position.x();
            t[1] = camera.Position.y();
            t[2] = camera.Position.z();
            glUniform3fv(glGetUniformLocation(programID, "viewPos"), 1, t);
            t[0] = 0.5f;
            t[1] = 0.5f;
            t[2] = 0.5f;
            glUniform3fv(glGetUniformLocation(programID, "light.ambient"), 1, t);
            t[0] = 0.8f;
            t[1] = 0.8f;
            t[2] = 0.8f;
            glUniform3fv(glGetUniformLocation(programID, "light.diffuse"), 1, t);
            t[0] = 1.0f;
            t[1] = 1.0f;
            t[2] = 1.0f;
            glUniform3fv(glGetUniformLocation(programID, "light.specular"), 1, t);
            t[0] = TS.orgColor.x();
            t[1] = TS.orgColor.y();
            t[2] = TS.orgColor.z();
            glUniform3fv(glGetUniformLocation(programID, "material.ambient"), 1, t);
            glUniform3fv(glGetUniformLocation(programID, "material.diffuse"), 1, t);
            // 适当弱化镜面光
            t[0] = 0.5f;
            t[1] = 0.5f;
            t[2] = 0.5f;
            glUniform3fv(glGetUniformLocation(programID, "material.specular"), 1, t);
            glUniform1f(glGetUniformLocation(programID, "material.shininess"), 32.0f);
        } else if (LT.l_type == 3) { // spot light
            glUniform1i(glGetUniformLocation(programID, "texType"), 6);
            t[0] = camera.Position.x();
            t[1] = camera.Position.y();
            t[2] = camera.Position.z();
            glUniform3fv(glGetUniformLocation(programID, "light.position"), 1, t);
            glUniform3fv(glGetUniformLocation(programID, "viewPos"), 1, t);
            t[0] = camera.Front.x();
            t[1] = camera.Front.y();
            t[2] = camera.Front.z();
            glUniform3fv(glGetUniformLocation(programID, "light.direction"), 1, t);
            glUniform1f(glGetUniformLocation(programID, "light.cutOff"), cos(qDegreesToRadians(12.5f)));
            glUniform1f(glGetUniformLocation(programID, "light.outerCutOff"), cos(qDegreesToRadians(20.5f)));
            // light properties 光线衰减使可视距离为50单位
            glUniform1f(glGetUniformLocation(programID, "light.constant"), 1.0f);
            glUniform1f(glGetUniformLocation(programID, "light.linear"), 0.09f);
            glUniform1f(glGetUniformLocation(programID, "light.quadratic"), 0.032f);
            t[0] = 0.1f;
            t[1] = 0.1f;
            t[2] = 0.1f;
            glUniform3fv(glGetUniformLocation(programID, "light.ambient"), 1, t);
            t[0] = 0.8f;
            t[1] = 0.8f;
            t[2] = 0.8f;
            glUniform3fv(glGetUniformLocation(programID, "light.diffuse"), 1, t);
            t[0] = 1.0f;
            t[1] = 1.0f;
            t[2] = 1.0f;
            glUniform3fv(glGetUniformLocation(programID, "light.specular"), 1, t);
            t[0] = TS.orgColor.x();
            t[1] = TS.orgColor.y();
            t[2] = TS.orgColor.z();
            glUniform3fv(glGetUniformLocation(programID, "material.ambient"), 1, t);
            glUniform3fv(glGetUniformLocation(programID, "material.diffuse"), 1, t);
            // 适当弱化镜面光
            t[0] = 0.5f;
            t[1] = 0.5f;
            t[2] = 0.5f;
            glUniform3fv(glGetUniformLocation(programID, "material.specular"), 1, t);
            glUniform1f(glGetUniformLocation(programID, "material.shininess"), 32.0f);
        }

        glBindVertexArray(VAO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_tex), vertices_tex, GL_STATIC_DRAW);
        // position attribute
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        // fragment normal attribute
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
        glEnableVertexAttribArray(1);
        // texture coord attribute
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
        glEnableVertexAttribArray(2);
        // 绘制立方体 cover mode
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);
    } else if (DT.type == 2) {
        glBindVertexArray(VAO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        // position attribute
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        // color attribute
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
        glEnableVertexAttribArray(1);
        // 绘制立方体 line mode
        glBindVertexArray(VAO);
        glEnable(GL_LINE_SMOOTH);
        glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST/*GL_NICEST*/); // 抗锯齿
        glDrawArrays(GL_LINE_STRIP, 0, 30);
    } else if (DT.type == 1) {
        glBindVertexArray(VAO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        // position attribute
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        // color attribute
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
        glEnableVertexAttribArray(1);
        // 绘制立方体 point mode
        glBindVertexArray(VAO);
        glEnable(GL_POINT_SMOOTH);
        glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST/*GL_NICEST*/); // 抗锯齿
        glDrawArrays(GL_POINTS, 0, 30);
    }

}

void tdglWidget::resizeGL(int w, int h)
{
    SCR_WIDTH = w;
    SCR_HEIGHT = h;
    glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
}

void tdglWidget::mousePressEvent(QMouseEvent *event) {
    MT.lastX = event->x();
    MT.lastY = event->y();
}


void tdglWidget::initFpsCamera()
{
    camera.initFpsCamera();
}

void tdglWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (MT.move_type == 1) {
        // fps camera
        MT.xoffset = event->x() - MT.lastX;
        MT.yoffset = MT.lastY - event->y(); // reversed since y-coordinates go from bottom to top
        MT.lastX = event->x();
        MT.lastY = event->y();
        camera.ProcessMouseMovement(MT.xoffset, MT.yoffset);
    } else if (MT.move_type == 2) {
        // rotate
        float sensitivity = 0.2f;
        MT.r_xoffset += sensitivity * (event->x() - MT.lastX);
        MT.r_yoffset += sensitivity * (MT.lastY - event->y()); // reversed since y-coordinates go from bottom to top
        MT.lastX = event->x();
        MT.lastY = event->y();
        camera.ProcessRotation(MT.r_xoffset, MT.r_yoffset);
    }

    update();
}

void tdglWidget::mouseReleaseEvent(QMouseEvent *event) {
    Q_UNUSED(event);
    update();
}

void tdglWidget::wheelEvent(QWheelEvent *event) {
    //camera.Position += event->delta() * 0.0012 * camera.Front;
    // 缩放功能
    camera.ProcessMouseScroll(event->delta() * 0.012);
    update();
}

void tdglWidget::timerEvent(QTimerEvent *event)
{
    // for animation
    Q_UNUSED(event);
    update();
}

void tdglWidget::setXpositive()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(len, 0.0f, 0.0f));
    update();
}

void tdglWidget::setXnegative()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(-len, 0.0f, 0.0f));
    update();
}

void tdglWidget::setYpositive()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(0.0f, len, 0.0001f));
    update();
}

void tdglWidget::setYnegative()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(0.0f, -len, 0.0001f));
    update();
}

void tdglWidget::setZpositive()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(0.0f, 0.0f, len));
    update();
}

void tdglWidget::setZnegative()
{
    float len = camera.Position.length();
    camera.setPosition(QVector3D(0.0f, 0.0f, -len));
    update();
}

void tdglWidget::keyPressEvent(QKeyEvent *event)
{
    float cameraSpeed = 0.025;
    if (event->key() == Qt::Key_W)
        camera.Position += cameraSpeed * camera.Front.normalized();
    if (event->key() == Qt::Key_S)
        camera.Position -= cameraSpeed * camera.Front.normalized();
    if (event->key() == Qt::Key_A)
        camera.Position -= QVector3D::crossProduct(camera.Front, camera.Up).normalized() * cameraSpeed;
    if (event->key() == Qt::Key_D)
        camera.Position += QVector3D::crossProduct(camera.Front, camera.Up).normalized() * cameraSpeed;
    update();
}

